/**********************************************************************
 * Filename: Campeonato.java
 * Created: 2012/04/30
 * @author Fernando Brito e Abreu
 **********************************************************************/
package pt.spw.copaPaises.businessLayer;

import java.util.Set;
import java.util.HashSet;


public class Campeonato {
	public static Set<Campeonato> allInstances = new HashSet<Campeonato>();

	private CalendarDate data_inicio;
	private TipoCampeonato genero;
	private String nome;

	/**********************************************************************
	 * Default constructor
	 **********************************************************************/
	public Campeonato() {
		allInstances.add(this);
	}

	/**********************************************************************
	 * Parameterized constructor
	 * 
	 * @param data_inicio
	 *            the data_inicio to initialize
	 * @param genero
	 *            the genero to initialize
	 * @param nome
	 *            the nome to initialize
	 **********************************************************************/
	public Campeonato(CalendarDate data_inicio, TipoCampeonato genero,
			String nome) {
		this.data_inicio = data_inicio;
		this.genero = genero;
		this.nome = nome;
		allInstances.add(this);
	}

	/**********************************************************************
	 * Standard attribute getter
	 * 
	 * @return the data_inicio of the campeonato
	 **********************************************************************/
	public CalendarDate data_inicio() {
		return data_inicio;
	}

	/**********************************************************************
	 * Standard attribute setter
	 * 
	 * @param data_inicio
	 *            the data_inicio to set
	 **********************************************************************/
	public void setData_inicio(CalendarDate data_inicio) {
		this.data_inicio = data_inicio;
	}

	/**********************************************************************
	 * Standard attribute getter
	 * 
	 * @return the genero of the campeonato
	 **********************************************************************/
	public TipoCampeonato genero() {
		return genero;
	}

	/**********************************************************************
	 * Standard attribute setter
	 * 
	 * @param genero
	 *            the genero to set
	 **********************************************************************/
	public void setGenero(TipoCampeonato genero) {
		this.genero = genero;
	}

	/**********************************************************************
	 * Standard attribute getter
	 * 
	 * @return the nome of the campeonato
	 **********************************************************************/
	public String nome() {
		return nome;
	}

	/**********************************************************************
	 * Standard attribute setter
	 * 
	 * @param nome
	 *            the nome to set
	 **********************************************************************/
	public void setNome(String nome) {
		this.nome = nome;
	}

	/**********************************************************************
	 * ONE2MANY getter for Campeonato[1] <-> Grupo[*]
	 * 
	 * @return the grupos of the campeonato
	 **********************************************************************/
	public Set<Grupo> grupos() {
		Set<Grupo> result = new HashSet<Grupo>();
		for (Grupo x : Grupo.allInstances)
			if (x.campeonato() == this)
				result.add(x);
		return result;
	}

	/**********************************************************************
	 * ONE2MANY setter for Campeonato[1] <-> Grupo[*]
	 * 
	 * @param grupos
	 *            the grupos to set
	 **********************************************************************/
	public void setGrupos(Set<Grupo> grupos) {
		for (Grupo x : grupos)
			x.setCampeonato(this);
	}

	/**********************************************************************
	 * MEMBER2ASSOCIATIVE getter for Campeonato[*] <-> Participacao[2..*]
	 * 
	 * @return the participacao of the participado
	 **********************************************************************/
	public Set<Participacao> participacao() {
		Set<Participacao> result = new HashSet<Participacao>();
		for (Participacao x : Participacao.allInstances)
			if (x.participado() == this)
				result.add(x);
		return result;
	}

	/**********************************************************************
	 * MEMBER2ASSOCIATIVE setter for Campeonato[*] <-> Participacao[2..*]
	 * 
	 * @param participacao
	 *            the participacao to set
	 **********************************************************************/
	public void setParticipacao(Set<Participacao> participacao) {
		for (Participacao x : participacao)
			x.setParticipado(this);
	}

	/**********************************************************************
	 * MEMBER2MEMBER getter for Campeonato[*] <-> Pais[2..*]
	 * 
	 * @return the participante of the participado
	 **********************************************************************/
	public Set<Pais> participante() {
		Set<Pais> result = new HashSet<Pais>();
		for (Participacao x : Participacao.allInstances)
			if (x.participado() == this)
				result.add(x.participante());
		return result;
	}

	/**********************************************************************
	 * MEMBER2MEMBER setter for Campeonato[*] <-> Pais[2..*]
	 * 
	 * @param participante
	 *            the participante to set
	 **********************************************************************/
	public void setParticipante(Set<Pais> participante) {
		for (Pais t : participante)
			for (Participacao x : Participacao.allInstances)
				if (x.participado() == this)
					x.setParticipante(t);
	}

	/**********************************************************************
	 * User-defined operation specified in SOIL/OCL
	 **********************************************************************/
	public void criaMembrosEquipas() {
		// TODO
		// for eq in self.grupos->collectNested($e : Grupo |
		// $e.jogos)->flatten->collectNested($e : Jogo |
		// $e.equipa)->flatten->asSet do eq.criaFichaJogoFalsa() end;
	}

	/**********************************************************************
	 * User-defined operation specified in SOIL/OCL
	 * 
	 * @param nome
	 *            the nome to set
	 * @param genero
	 *            the genero to set
	 * @param paises
	 *            the paises to set
	 * @param grupos
	 *            the grupos to set
	 **********************************************************************/
	public void inicializa(String nome, TipoCampeonato genero, String paises,
			String grupos) {
		// TODO
		// self.nome := nome;
		// self.genero := genero;
		// for p in paises do participacao := new Participacao(((p + '_') +
		// self.nome)) between (new Pais(p),self);
		// participacao.organizador := false;
		// treinador := new Treinador(('Treinador_' + p));
		// insert (treinador,participacao) into Treinador_Participacao end;
		// for g in grupos do insert (self,new Grupo(g)) into Campeonato_Grupo
		// end;
	}

	/**********************************************************************
	 * User-defined operation specified in SOIL/OCL
	 * 
	 * @param data
	 *            the data to set
	 **********************************************************************/
	public void inicioCampeonato(CalendarDate data) {
		data_inicio = data;
		// data_inicio := data;
	}

	protected static class DataIndefinida {
		protected boolean before(Campeonato campeonato) {
			return campeonato.data_inicio == null;
		}
	}

	// PRE-CONDITIONS (TODO)
	/*
	 * pre DataIndefinida self.data_inicio.isUndefined
	 */

	/**********************************************************************
	 * User-defined operation specified in SOIL/OCL
	 **********************************************************************/
	public double mediaGolosPorJogo() {
		int n_golos = 0;
		int n_jogos = 0;
		for (Grupo g : grupos())
			for (Jogo j : g.jogos()) {
				n_jogos++;
				for (Equipa e : j.equipa())
					for (Membro m : e.membro())
						n_golos += m.golos().size();
			}
		// return (self.grupos->collectNested($e : Grupo |
		// $e.jogos)->flatten->collectNested($e : Jogo |
		// $e.equipa)->flatten->collectNested($e : Equipa |
		// $e.membro)->flatten->collectNested($e : Membro |
		// $e.golos)->flatten->size / self.grupos->collectNested($e : Grupo |
		// $e.jogos)->flatten->size)
		return (double)n_golos / (double)n_jogos;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	/**********************************************************************
	 * Object serializer
	 **********************************************************************/
	public String toString() {
		return "Campeonato [data_inicio=" + data_inicio + ", genero=" + genero
				+ ", nome=" + nome + "]\n";
	}

	protected static class CampeonatoEuropeuPaisesEuropeus {
		boolean inv = false;
	    protected boolean check(Campeonato c) {
	    	if(c.genero().equals(TipoCampeonato.Europeu)){
	    		for(Pais p : c.participante())
	    			if(p.continente().nome().equals("Europa"))
	    				inv = true;
	    	}else{
	    		for(Pais p : c.participante())
	    			if(!p.continente().nome().equals("Europa"))
	    				inv = true;
	    	}
	      return inv;
	    }
	  }

	protected static class UmTreinadorPorPais {
		boolean inv = false;
	    protected boolean check(Campeonato c) {
	    	int n_treinadores = 0;
			for (Participacao p : c.participacao())
				if (p.treinador() != null)
					n_treinadores++;
			if (n_treinadores == c.participante().size())
				inv = true;
	      return inv;
	    }
	  }

	protected static class NacionaisDeUmPais {
		boolean inv = false;
	    protected boolean check(Campeonato c) {
	    	for(Pais p : c.participante())
	    		if(p.nacionais().size() == 24)
	    			inv = true;
	      return inv;
	    }
	  }

	protected static class NumeroGrupos {
		boolean inv = false;
	    protected boolean check(Campeonato c) {
	    		if(c.grupos().size() == c.participante().size()/4)
	    			inv = true;
	      return inv;
	    }
	  }

	// -------------------------------------------------------------------------------
	// INVARIANTS (TODO)
	/*
	 * inv CampeonatoEuropeuPaisesEuropeus if (self.genero =
	 * TipoCampeonato::Europeu) then self.participante->collectNested($e : Pais
	 * | $e.continente)->forAll(c : Continente | (c.nome = 'Europa')) else
	 * self.participante->collectNested($e : Pais | $e.continente)->exists(c2 :
	 * Continente | not (c2.nome = 'Europa')) endif
	 * 
	 * inv UmTreinadorPorPais (self.participacao->collectNested($e :
	 * Participacao | $e.treinador)->size = self.participante->size)
	 * 
	 * inv NacionaisDeUmPais (self.participante->collectNested($e : Pais |
	 * $e.nacionais)->flatten->size = 24)
	 * 
	 * inv NumeroGrupos (self.grupos->select(g : Grupo | (g.fase =
	 * FaseCampeonato::Grupos))->size = (self.participante->size / 4))
	 */
}
